﻿Public Class Life

    Dim CurrentCreature, InitialCreatures, MaxCreatures, WindowWidth, WindowHeight, Cycles, NumberOfCreatures, Time As Integer
    Dim Creatures(1000, 10) As String
    Dim Finished As Boolean = False
    Dim CreaturePictureBoxes(1000), BoomBox As PictureBox
    Dim CreatureColours(100) As ConsoleColor
    Dim CreatureImages(100) As System.Drawing.Bitmap
    Dim Randomer As New Random
    Dim Debug As String = ""

    Private Sub Life_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        If e.KeyCode = Keys.Down Then
            If pbHealer.Location.Y + 20 <= WindowHeight Then
                pbHealer.Location = New System.Drawing.Point(pbHealer.Location.X, pbHealer.Location.Y + 20)
            End If
        ElseIf e.KeyCode = Keys.Up Then
            If pbHealer.Location.Y - 20 >= 0 Then
                pbHealer.Location = New System.Drawing.Point(pbHealer.Location.X, pbHealer.Location.Y - 20)
            End If
        ElseIf e.KeyCode = Keys.Left Then
            If pbHealer.Location.X - 20 >= 0 Then
                pbHealer.Location = New System.Drawing.Point(pbHealer.Location.X - 20, pbHealer.Location.Y)
            End If
        ElseIf e.KeyCode = Keys.Right Then
            If pbHealer.Location.X + 20 <= WindowWidth Then
                pbHealer.Location = New System.Drawing.Point(pbHealer.Location.X + 20, pbHealer.Location.Y)
            End If
        End If

        For DiseaseCount As Integer = 0 To MaxCreatures
            If DistanceBetweenNumbers(pbHealer.Location.X, Creatures(DiseaseCount, 0)) < 50 And DistanceBetweenNumbers(pbHealer.Location.Y, Creatures(DiseaseCount, 1)) < 50 Then
                Creatures(DiseaseCount, 3) = 0
                Creatures(DiseaseCount, 2) = 0
            End If
        Next
    End Sub

    Private Sub Life_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' Creature Structure
        ' 0 - x
        ' 1 - y
        ' 2 - type
        ' 3 - life
        ' 4 - dead (cleared)
        ' 5 - movement direction

        ' Types
        ' 0 - new
        ' 1 - over 100
        ' 2 - over 200
        ' 3 - over 300
        ' 4 - over 400
        ' 5 - diseased

        ' Colours
        CreatureImages(0) = Global.LifeGeneratorGUI.My.Resources.Resources.creature1
        CreatureImages(1) = Global.LifeGeneratorGUI.My.Resources.Resources.creature1
        CreatureImages(2) = Global.LifeGeneratorGUI.My.Resources.Resources.creature2
        CreatureImages(3) = Global.LifeGeneratorGUI.My.Resources.Resources.creature3
        CreatureImages(4) = Global.LifeGeneratorGUI.My.Resources.Resources.creature4
        CreatureImages(5) = Global.LifeGeneratorGUI.My.Resources.Resources.creature5

        ' Config
        WindowWidth = 980
        WindowHeight = 780

        MaxCreatures = 151
        CurrentCreature = 0
        InitialCreatures = 20

        Cycles = 0

        ' Explosion preparation
        BoomBox = New PictureBox

        ' Give the creature the basic values
        BoomBox.BackColor = System.Drawing.Color.Transparent
        BoomBox.Image = Global.LifeGeneratorGUI.My.Resources.Resources.boom
        BoomBox.Location = New System.Drawing.Point(0, 0)
        BoomBox.Name = "KABOOOOOOOM"
        BoomBox.Size = New System.Drawing.Size(200, 200)
        BoomBox.SizeMode = PictureBoxSizeMode.StretchImage
        BoomBox.TabIndex = 0
        BoomBox.TabStop = False
        BoomBox.Visible = False

        ' Attach it to the window
        Me.Controls.Add(BoomBox)

        ' Initialise creature array
        For Count As Integer = 0 To MaxCreatures
            ' Give the creature some basic values
            Creatures(Count, 0) = Randomer.Next(0, WindowWidth)
            Creatures(Count, 1) = Randomer.Next(0, WindowHeight)
            Creatures(Count, 2) = 0
            Creatures(Count, 3) = 0

            ' Create a new PictureBox object for the creature
            CreaturePictureBoxes(Count) = New PictureBox

            ' If we haven't made enough for initial creatures, make the creature alive.
            ' Otherwise, create it as dead.
            If Count > (InitialCreatures - 1) Then
                Creatures(Count, 4) = "yes"
                CreaturePictureBoxes(Count).Visible = False
            Else
                Creatures(Count, 4) = "no"
                CreaturePictureBoxes(Count).Visible = True
            End If

            ' Give the creature the basic values
            CreaturePictureBoxes(Count).BackColor = System.Drawing.Color.Transparent
            CreaturePictureBoxes(Count).Image = Global.LifeGeneratorGUI.My.Resources.Resources.creature1
            CreaturePictureBoxes(Count).Location = New System.Drawing.Point(Creatures(Count, 0), Creatures(Count, 1))
            CreaturePictureBoxes(Count).Name = "pbCreature" & Count
            CreaturePictureBoxes(Count).Size = New System.Drawing.Size(20, 20)
            CreaturePictureBoxes(Count).TabIndex = 0
            CreaturePictureBoxes(Count).TabStop = False

            ' Attach it to the window
            Me.Controls.Add(CreaturePictureBoxes(Count))
        Next

        ' Intro
        MsgBox("Welcome to LifeGeneratorGUI!" & vbNewLine & vbNewLine & "Colour Codes:" & vbNewLine & "Light Green -> New" & vbNewLine & "Blue -> Young" & vbNewLine & "Dark Blue -> Older" & vbNewLine & "Dark Green -> Older Yet" & vbNewLine & "Red -> Diseased")
        ' Run through a loop to get the interval at which the creatures operate
        Do While GetTime() = False
        Loop

        ' Start the timer
        timLifeTimer.Enabled = True
    End Sub

    Private Function GetTime()
        ' This function gets a number for use as the time interval

        ' Prompt the user for a speed
        Console.Write(vbTab & "Enter a speed (1 - 50), lowest is faster: ")
        Dim RawInput As String = InputBox("Enter a speed (lowest = fastest) [1-50]", "Speed Selection", "5")

        ' Test if the speed entered is a number
        If IsNumeric(RawInput) Then
            ' Check if the speed is within a range (1 - 50)
            If Int(RawInput) > 0 And Int(RawInput) < 51 Then
                ' Speed is OK, return true (to continue) and set the interval of the timer
                timLifeTimer.Interval = Int(RawInput)
                Return True
            Else
                ' Speed is out of the range. Return false to run this function again
                MsgBox("The number must be between 1 and 50.", MsgBoxStyle.Exclamation, "Error")
                Return False
            End If
        Else
            ' What the user entered isn't a number, return false for the function to be run again
            MsgBox("That isn't a number!", MsgBoxStyle.Exclamation, "Error")
            Return False
        End If
    End Function

    Private Function CreateCreature(ByVal X As Integer, ByVal Y As Integer, ByVal Type As String, ByVal Life As String)
        ' The function for creating creatures. It re-uses the dead creatures in our array (which can hold 1000)

        ' Loop through the current creatures

        For Count = 0 To MaxCreatures
            ' Is it dead?
            If Creatures(Count, 4) = "yes" Then
                ' Yes it is! Reset the values
                Creatures(Count, 0) = X
                Creatures(Count, 1) = Y
                Creatures(Count, 2) = Type
                Creatures(Count, 3) = Life
                Creatures(Count, 4) = "no"
                Creatures(Count, 5) = Randomer.Next(0, 5)

                ' Setup the actual PictureBox object
                CreaturePictureBoxes(Count).Image = CreatureImages(0)
                CreaturePictureBoxes(Count).Visible = True

                ' Increment the number of creatures
                NumberOfCreatures = NumberOfCreatures + 1
                Exit For
            End If
        Next

        Return True
    End Function

    Private Function Life()
        ' The function run repeatedly to handle all aspects of life

        ' Prepare a variable for couting if any creatures are alive
        Dim Alive As Integer = 0

        If BoomBox.Visible = True Then
            System.Threading.Thread.Sleep(100)
            BoomBox.Visible = False
        End If

        ' Loop through the creatures
        For Count = 0 To MaxCreatures
            ' Check if the creature is dead
            If Creatures(Count, 4) = "no" Then
                ' Death - if the age is above 1000
                If Creatures(Count, 3) > 1000 Then
                    ' Make the creature hidden and reset its values.
                    CreaturePictureBoxes(Count).Visible = False
                    Creatures(Count, 0) = 0
                    Creatures(Count, 1) = 0
                    Creatures(Count, 2) = 0
                    Creatures(Count, 3) = 0
                    Creatures(Count, 4) = 0
                    Creatures(Count, 5) = 0

                    ' Decrement the number of creatures
                    NumberOfCreatures = NumberOfCreatures - 1
                Else
                    ' Age
                    Creatures(Count, 3) = Creatures(Count, 3) + 1

                    ' Initial values requiring re-rendering
                    Dim InitialX, InitialY, InitialType As Integer
                    InitialX = Creatures(Count, 0)
                    InitialY = Creatures(Count, 1)
                    InitialType = Creatures(Count, 2)

                    ' Type Changes
                    ' Check if the creature has a type above 4 (reserved for special purposes and so should not be changed with age)
                    If Int(Creatures(Count, 2)) <= 4 Then
                        ' Age based type changes
                        If Creatures(Count, 3) > 400 Then
                            Creatures(Count, 2) = "4"
                        ElseIf Creatures(Count, 3) > 300 Then
                            Creatures(Count, 2) = "3"
                        ElseIf Creatures(Count, 3) > 200 Then
                            Creatures(Count, 2) = "2"
                        ElseIf Creatures(Count, 3) > 100 Then
                            Creatures(Count, 2) = "1"
                        End If
                    End If

                    ' Movement - make a random number, if that number equals a value, move in a certain direction
                    If Randomer.Next(0, 10) = 5 Then
                        Creatures(Count, 5) = Randomer.Next(0, 5)
                    End If

                    Dim MovementDistance As Integer = 10

                    If (Creatures(Count, 0) + MovementDistance + 20) < WindowWidth And Creatures(Count, 5) = 0 Then
                        Creatures(Count, 0) = Creatures(Count, 0) + MovementDistance
                    ElseIf (Creatures(Count, 0) - MovementDistance - 20) >= 0 And Creatures(Count, 5) = 1 Then
                        Creatures(Count, 0) = Creatures(Count, 0) - MovementDistance
                    ElseIf (Creatures(Count, 1) + MovementDistance + 20) < WindowHeight And Creatures(Count, 5) = 2 Then
                        Creatures(Count, 1) = Creatures(Count, 1) + MovementDistance
                    ElseIf (Creatures(Count, 1) - MovementDistance - 20) >= 0 And Creatures(Count, 5) = 3 Then
                        Creatures(Count, 1) = Creatures(Count, 1) - MovementDistance
                    End If

                    ' Accidents - random incidence of ageing to make the simulation more interesting
                    Dim AccidentRandom As Integer = Randomer.Next(0, 35)
                    If AccidentRandom = 13 Then
                        Creatures(Count, 3) = Creatures(Count, 3) + 5
                    End If

                    ' Disease - regenerates the creature
                    Dim DiseaseRandom As Integer = Randomer.Next(0, 500)
                    ' If the creature is older than 300 and the disease random triggers (1 in 500 chance)
                    If Creatures(Count, 3) > 350 And Creatures(Count, 3) < 360 And DiseaseRandom = 250 Then
                        ' Make the creature type 5 (diseased)
                        Creatures(Count, 2) = 5
                    End If

                    ' If the creature is older than 600 and is diseased, regenerate it
                   If Creatures(Count, 3) > 600 And Creatures(Count, 2) = "5" Then
                        Creatures(Count, 3) = 1001
                    End If

                    ' If the creature is diseased, spread the lulz
                    If Creatures(Count, 2) = 5 Then
                        For DiseaseCount As Integer = 0 To MaxCreatures
                            ' SPREAD IT
                            If DistanceBetweenNumbers(Creatures(Count, 0), Creatures(DiseaseCount, 0)) < 20 And DistanceBetweenNumbers(Creatures(Count, 1), Creatures(DiseaseCount, 1)) < 20 Then
                                Creatures(DiseaseCount, 2) = 5
                            End If
                        Next
                    End If

                    ' Division
                    Dim DivisionRandom As Integer = Randomer.Next(0, 100)
                    ' If the creature is between 500 and 600 and the division random triggers (1 in 100)
                    If Creatures(Count, 3) > 500 And Creatures(Count, 3) < 800 And DivisionRandom = 50 And Creatures(Count, 2) <> 5 Then
                        ' Create a new creature
                        CreateCreature(Creatures(Count, 0) + 20, Creatures(Count, 1), 0, Creatures(Count, 3) / 2)
                    End If

                    ' Update character
                    ' If the creature has been moved
                    If InitialX <> Int(Creatures(Count, 0)) Or InitialY <> Int(Creatures(Count, 1)) Then
                        MoveCreature(Count, Creatures(Count, 0), Creatures(Count, 1))
                    End If
                    ' Or if the creature's type has changed
                    If InitialType <> Creatures(Count, 2) Then
                        DrawCreature(Count)
                    End If

                    ' We've processed a live creature, add 1 to alive
                    Alive = Alive + 1
                End If
            End If
        Next

        ' If no alive creatures were processed (they're all dead), we've finished
        If Alive = 0 Then
            btnRestart.Visible = True
        Else
            ' At least one creature was processed, add 1 cycle
            Cycles = Cycles + 1
        End If

        lblCycle.Text = "Cycles: " & Str(Cycles)
        lblCreatures.Text = "Alive: " & Str(Alive) & "/" & (MaxCreatures - 1) & " (" & Math.Round(Alive / (MaxCreatures - 1) * 100) & "%)"
        Return True
    End Function

    Private Function MoveCreature(ByVal CreatureIndex As Integer, ByVal NewX As Integer, ByVal NewY As Integer)
        ' A function for handling the movement of a creature

        ' Check the creature is valid
        If IsNothing(Creatures(CreatureIndex, 2)) = False Then
            ' Update the X and Y of the creature
            Creatures(CreatureIndex, 0) = NewX
            Creatures(CreatureIndex, 1) = NewY

            ' Draw the creature again
            DrawCreature(CreatureIndex)
        End If

        Return True

    End Function

    Private Function DrawCreature(ByVal Creature As Integer)
        ' Drawing of the creature (moving the relevant picture box)

        ' Check if the creature is valid
        If IsNothing(Creatures(Creature, 2)) = False Then
            ' Move the creature and update its image (which will change if the type has been altered during Life())
            CreaturePictureBoxes(Creature).Location = New System.Drawing.Point(Creatures(Creature, 0), Creatures(Creature, 1))
            CreaturePictureBoxes(Creature).Image = CreatureImages(Creatures(Creature, 2))
        End If

        Return True
    End Function

    Private Sub timLifeTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timLifeTimer.Tick
        ' Tick away during Life
        Life()
    End Sub

    Private Sub btnRestart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRestart.Click
        Cycles = 0

        btnRestart.Visible = False

        For Count = 0 To InitialCreatures
            CreateCreature(Randomer.Next(Randomer.Next(0, WindowWidth)), Randomer.Next(0, WindowHeight), 0, 0)
        Next

    End Sub

    Private Function DistanceBetweenNumbers(ByVal NumberOne As Double, ByVal NumberTwo As Integer)
        If NumberOne > NumberTwo Then
            Return NumberOne - NumberTwo
        ElseIf NumberOne = NumberTwo Then
            Return 0
        Else
            Return NumberTwo - NumberOne
        End If
    End Function

    Private Sub Life_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
        ' Nuke
        BoomBox.Location = New System.Drawing.Point(e.X - 83, e.Y - 81)
        BoomBox.Visible = True

        For DiseaseCount As Integer = 0 To MaxCreatures
            If DistanceBetweenNumbers(e.X, Creatures(DiseaseCount, 0)) < 166 And DistanceBetweenNumbers(e.Y, Creatures(DiseaseCount, 1)) < 162 Then
                Creatures(DiseaseCount, 3) = 1001
            End If
        Next
    End Sub
End Class